; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt -S -passes=gvn-hoist < %s | FileCheck %s

; Check that the call and fcmp are hoisted.
define void @fun(float %__b) minsize {
; CHECK-LABEL: @fun(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    br label [[IF_THEN:%.*]]
; CHECK:       if.then:
; CHECK-NEXT:    [[TMP0:%.*]] = call float @llvm.fabs.f32(float [[__B:%.*]])
; CHECK-NEXT:    [[CMPINF7:%.*]] = fcmp oeq float [[TMP0]], 0x7FF0000000000000
; CHECK-NEXT:    br i1 undef, label [[IF_THEN8:%.*]], label [[LOR_LHS_FALSE:%.*]]
; CHECK:       lor.lhs.false:
; CHECK-NEXT:    unreachable
; CHECK:       if.then8:
; CHECK-NEXT:    ret void
;
entry:
  br label %if.then

if.then:                                          ; preds = %entry
  br i1 undef, label %if.then8, label %lor.lhs.false

lor.lhs.false:                                    ; preds = %if.then
  %0 = call float @llvm.fabs.f32(float %__b) #2
  %cmpinf7 = fcmp oeq float %0, 0x7FF0000000000000
  unreachable

if.then8:                                         ; preds = %if.then
  %1 = call float @llvm.fabs.f32(float %__b) #2
  %cmpinf10 = fcmp oeq float %1, 0x7FF0000000000000
  ret void
}

declare float @llvm.fabs.f32(float)

; Check that extractvalues are not hoisted into entry, but that non-dependent
; adds are.
define i32 @foo(i32 %x) {
; CHECK-LABEL: @foo(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[ADD:%.*]] = add nsw i32 [[X:%.*]], 1
; CHECK-NEXT:    [[TMP0:%.*]] = callbr { i32, i32 } asm sideeffect "somestuff", "=r,=r,!i"()
; CHECK-NEXT:    to label [[ASM_FALLTHROUGH:%.*]] [label %err.split]
; CHECK:       asm.fallthrough:
; CHECK-NEXT:    [[ASMRESULT:%.*]] = extractvalue { i32, i32 } [[TMP0]], 0
; CHECK-NEXT:    ret i32 [[ADD]]
; CHECK:       err.split:
; CHECK-NEXT:    [[ASMRESULT2:%.*]] = extractvalue { i32, i32 } [[TMP0]], 0
; CHECK-NEXT:    ret i32 [[ADD]]
;
entry:
  %0 = callbr { i32, i32 } asm sideeffect "somestuff", "=r,=r,!i"()
  to label %asm.fallthrough [label %err.split]

asm.fallthrough:                                  ; preds = %entry
  %asmresult = extractvalue { i32, i32 } %0, 0
  %add = add nsw i32 %x, 1
  ret i32 %add

err.split:                                        ; preds = %entry
  %asmresult2 = extractvalue { i32, i32 } %0, 0
  %add2 = add nsw i32 %x, 1
  ret i32 %add2
}

; Check that extractvalues and dependent adds are not hoisted into entry.
define i32 @foo2() {
; CHECK-LABEL: @foo2(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[TMP0:%.*]] = callbr { i32, i32 } asm sideeffect "somestuff", "=r,=r,!i"()
; CHECK-NEXT:    to label [[ASM_FALLTHROUGH:%.*]] [label %err.split]
; CHECK:       asm.fallthrough:
; CHECK-NEXT:    [[ASMRESULT:%.*]] = extractvalue { i32, i32 } [[TMP0]], 0
; CHECK-NEXT:    [[ADD:%.*]] = add nsw i32 [[ASMRESULT]], 1
; CHECK-NEXT:    ret i32 [[ADD]]
; CHECK:       err.split:
; CHECK-NEXT:    [[ASMRESULT2:%.*]] = extractvalue { i32, i32 } [[TMP0]], 0
; CHECK-NEXT:    [[ADD2:%.*]] = add nsw i32 [[ASMRESULT2]], 1
; CHECK-NEXT:    ret i32 [[ADD2]]
;
entry:
  %0 = callbr { i32, i32 } asm sideeffect "somestuff", "=r,=r,!i"()
  to label %asm.fallthrough [label %err.split]

asm.fallthrough:                                  ; preds = %entry
  %asmresult = extractvalue { i32, i32 } %0, 0
  %add = add nsw i32 %asmresult, 1
  ret i32 %add

err.split:                                        ; preds = %entry
  %asmresult2 = extractvalue { i32, i32 } %0, 0
  %add2 = add nsw i32 %asmresult2, 1
  ret i32 %add2
}

; Ensure we don't hoist loads that are modified by callbr.
@x = global i32 0
define i32 @foo3() {
; CHECK-LABEL: @foo3(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    callbr void asm "", "=*m,!i"(ptr elementtype(i32) @x)
; CHECK-NEXT:    to label [[ASM_FALLTHROUGH:%.*]] [label %err.split]
; CHECK:       asm.fallthrough:
; CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr @x, align 4
; CHECK-NEXT:    ret i32 [[TMP0]]
; CHECK:       err.split:
; CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr @x, align 4
; CHECK-NEXT:    ret i32 [[TMP1]]
;
entry:
  callbr void asm "", "=*m,!i"(ptr elementtype(i32) @x)
  to label %asm.fallthrough [label %err.split]

asm.fallthrough:                                  ; preds = %entry
  %0 = load i32, ptr @x
  ret i32 %0

err.split:                                        ; preds = %entry
  %1 = load i32, ptr @x
  ret i32 %1
}

; Ensure we do hoist loads that aren't modified by callbr, if the callbr has
; the attribute memory(argmem:readwrite).
@y = global i32 0
define i32 @foo4() {
; CHECK-LABEL: @foo4(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr @y, align 4
; CHECK-NEXT:    callbr void asm "", "=*m,!i"(ptr elementtype(i32) @x) #[[ATTR2:[0-9]+]]
; CHECK-NEXT:    to label [[A:%.*]] [label %b]
; CHECK:       a:
; CHECK-NEXT:    ret i32 [[TMP0]]
; CHECK:       b:
; CHECK-NEXT:    ret i32 [[TMP0]]
;
entry:
  callbr void asm "", "=*m,!i"(ptr elementtype(i32) @x) memory(argmem: readwrite)
  to label %a [label %b]

a:                                  ; preds = %entry
  %0 = load i32, ptr @y
  ret i32 %0

b:                                        ; preds = %entry
  %1 = load i32, ptr @y
  ret i32 %1
}

; Ensure we don't hoist loads that are modified by callbr, if the callbr has
; the attribute memory(argmem:readwrite).
define i32 @foo5() {
; CHECK-LABEL: @foo5(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    callbr void asm "", "=*m,!i"(ptr elementtype(i32) @x) #[[ATTR2]]
; CHECK-NEXT:    to label [[A:%.*]] [label %b]
; CHECK:       a:
; CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr @x, align 4
; CHECK-NEXT:    ret i32 [[TMP0]]
; CHECK:       b:
; CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr @x, align 4
; CHECK-NEXT:    ret i32 [[TMP1]]
;
entry:
  callbr void asm "", "=*m,!i"(ptr elementtype(i32) @x) memory(argmem: readwrite)
  to label %a [label %b]

a:                                  ; preds = %entry
  %0 = load i32, ptr @x
  ret i32 %0

b:                                        ; preds = %entry
  %1 = load i32, ptr @x
  ret i32 %1
}

; Ensure we hoist loads that are modified by callbr, if the callbr has the
; attribute memory(argmem:none).
define i32 @foo6() {
; CHECK-LABEL: @foo6(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr @x, align 4
; CHECK-NEXT:    callbr void asm "", "=*m,!i"(ptr elementtype(i32) @x) #[[ATTR3:[0-9]+]]
; CHECK-NEXT:    to label [[A:%.*]] [label %b]
; CHECK:       a:
; CHECK-NEXT:    ret i32 [[TMP0]]
; CHECK:       b:
; CHECK-NEXT:    ret i32 [[TMP0]]
;
entry:
  callbr void asm "", "=*m,!i"(ptr elementtype(i32) @x) memory(argmem: none)
  to label %a [label %b]

a:                                  ; preds = %entry
  %0 = load i32, ptr @x
  ret i32 %0

b:                                        ; preds = %entry
  %1 = load i32, ptr @x
  ret i32 %1
}

